#--
# Copyright (c) 2007 Maximilian Ott
# 
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
# 
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
# 
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.
#++
require 'fastercsv'

class BrowseController < ApplicationController

  # Different browse modes. Require related _xxx_result partial
  # 
  # Return an array of all the available modes. If the 
  # display name is different from the capitalized mode name, provide
  # an array of [mode, display_name]
  #
  # Examples: 
  #     ['list', 'stats']
  #     [['list', 'Listing'], ['stats', 'Statistics']]
  #     [] ... just listing
  #
  def self.modes
    []
  end
  
  # [klass, table, init_from, init_select, init_include]
  def _init
#    [Student, :students, [:students], 'students.*', [:person, :supervisors]]
    raise 'Implementation of "_init" missing.'
  end
  private :_init
  
  # Primary action for this controller. Returns a list
  # records defined by +_init+ and the current filter.
  #
  # Adding an optional parameter 'clear' will remove the
  # currently active filter and return a list of all the 
  # records.
  #
  def browse
    session['current_action'] = "browse";
    
    if (mode = params['mode'])
      session['browse_mode'] = mode
    end
    @mode = session['browse_mode'] || 'list'
    
    if params.delete('clear')
      clear_filter
#      session[:list_filter] = nil
#      session[:list_filter_scope] = nil
    end
    _list
  end

  # Called by an AJAX function to change the filter
  # settings. Will return a new list accordingly.
  #  
  def update_ajax
    _set_filter
    _list
    render :update do |page|
      page.replace_html 'listing', :partial => 'body'
    end
  end
  
  # this action allows a direct linking to a specific filter
  def filter
    
    # puts " --------------------- FILTER DECODE >>>> BLAH"
    
    unless params['encf'].nil?
      f_string  = Base64.decode64(params['encf'])
      split     = f_string.scan(/.\[([^\]]*)\]\[([^\]]*)\]=([^\&]*)/)
      
      f_hash = Hash.new do |hash,key|
        hash[key] = {}
      end
      
      # puts " ----------------- #{split.inspect}"
      
      split.each do |sub_arr|
        main_key  = sub_arr[0]
        sub_key   = sub_arr[1]
        value     = sub_arr[2]  
    
        f_hash[main_key][sub_key] = value
      end
      
      params['f'] = f_hash
    end
    
    unless params['f'].nil?
       # puts "--------------------- #{params['f'].first[1]['op'].inspect}"
       params['logic_op'] = params['f'].first[1]['op']
       _set_filter
    end  
    
    redirect_to :action => 'browse'
    return
  end    
  
  # The list command shows all the records
  # filtered by an expression stored in the session object
  # This command sets this filter function and returns a new list.
  #
  def update_list_filter
    _set_filter
    redirect_to :action => 'browse'
  end
  
  
  
  # Clear filter for this session
  #
  def clear_filter
    _clear_filter
    redirect_to :action => 'browse'
  end
  
  private 
  
  def _set_filter
    klass, table, init_from, init_select, init_include = _init()
    
    f = params['f']
    
    # if params['logic_op'].nil?
    #   params['logic_op'] = 'AND'

    session[:list_f_logic_op] = logic_op = params['logic_op']
    
    # puts "SET_FILTER>> #{f.inspect}"
    if !(f.kind_of? Hash)
      session[:list_f]      = nil
      session[:list_filter] = nil
      session[:list_filter_scope] = nil
      session[:list_f_logic_op]   = nil
      return
    end
    session[:list_f] = params['f'].to_hash
    fes = AppFilterElement.parse(f)
    # change the logic op in all filters to what is selected by the user
    fes.each do |fe| fe.op = logic_op end
    sparams = {:conditions => [], :from => []}
    fes.each do |fe| sparams = fe.to_sql(sparams) end
    sparams[:from] << init_from
    @f = session[:list_filter] = fes
#    @s = sparams; breakpoint
    puts "FES >> #{fes.inspect}"
    puts "SPARAMS >> #{sparams.inspect}"    
    cond = sparams[:conditions] = sparams[:conditions].join(' ')
    f = sparams[:from].collect do |f| f.to_s end
    sparams[:from] = f.uniq.join(',')
    
#    @s = sparams; breakpoint
    
    sparams[:select] = init_select
    @p = sparams
    # ---------
    @s = session[:list_filter_scope] = cond.empty? ? nil : sparams
#    puts "SQL2>>> #{@s}"
    #breakpoint
  end
  
  def _clear_filter
    session[:list_f] = nil
    session[:list_filter] = nil
    session[:list_filter_scope] = nil
    session[:list_f_logic_op]   = nil
    session[:list_order] = nil
    session[:list_direction] = nil
  end
  
  private

  def per_page
    20
  end
  
  def ordered_by
    nil
  end
  
  # Execute list find within filter scope
  #
  def _list(paged = true, scope = nil)
#    flash[:alert] = nil
    @scope = scope || session[:list_filter_scope] || {}
    per_page = params[:per_page] || per_page()
    klass, table, init_from, init_select, init_include = _init()    
#    @s = scope; @i = init_include; @t = table; breakpoint
    puts "SCOPE>> #{@scope.inspect}"
    begin
      klass.with_scope(:find => @scope) do
        opts = paged ? {:page => params[:page], :per_page => per_page} : {}
        order = ordered_by()
        opts[:order] = order if order
        opts[:include] = init_include if init_include
        @records = paged ? klass.send(:paginate, opts) : klass.send(:find, :all)
  #      @record_pages, @records = paginate table, :include => init_include, :per_page => 20
      end
    rescue Exception => ex
      logger.error "While executing 'list': #{ex}"
      _clear_filter
      flash[:alert] = "Reset Filter because of internal error #{ex}\n#{ex.backtrace}"
      _list(paged)
    end
  end

  def _count(opts = {})
    @scope = session[:list_filter_scope] || {}
    klass, table, init_from, init_select, init_include = _init()    
    begin
      klass.with_scope(:find => @scope) do
        return klass.send(:count, opts)
      end
    rescue Exception => ex
      logger.error "While executing 'list': #{ex}"
      _clear_filter
      flash[:alert] = 'Reset Filter because of internal error'
      _count
    end
  end

  
  
  
  def stream_csv(filename = params[:action] + ".csv")    

     #this is required if you want this to work with IE        
     if request.env['HTTP_USER_AGENT'] =~ /msie/i
       headers['Pragma'] = 'public'
       headers["Content-type"] = "text/plain" 
       headers['Cache-Control'] = 'no-cache, must-revalidate, post-check=0, pre-check=0'
       headers['Content-Disposition'] = "attachment; filename=\"#{filename}\"" 
       headers['Expires'] = "0" 
     else
       headers["Content-Type"] ||= 'text/csv'
       headers["Content-Disposition"] = "attachment; filename=\"#{filename}\"" 
     end

    render :text => Proc.new { |response, output|
      csv = FasterCSV.new(output, :row_sep => "\r\n") 
      yield csv
    }
  end
  
  
  
end
